/*
Algorithm Name: Keccak
Authors: Guido Bertoni, Joan Daemen, Michaël Peeters and Gilles Van Assche
Implementation by Ronny Van Keer, STMicroelectronics

This code, originally by Ronny Van Keer, is hereby put in the public domain.
It is given as is, without any guarantee.

For more information, feedback or questions, please refer to our website:
http://keccak.noekeon.org/
*/

#include "Keccak-avr8.h"
#include <avr/pgmspace.h>

#define cKeccakR_SizeInBytes    (cKeccakR / 8)
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include <ctype.h>

const char	testVectorMessage[] PROGMEM = 
	"\x3A\x3A\x81\x9C\x48\xEF\xDE\x2A\xD9\x14\xFB\xF0\x0E\x18\xAB\x6B\xC4\xF1\x45\x13\xAB\x27\xD0\xC1\x78\xA1\x88\xB6\x14\x31\xE7\xF5"
	"\x62\x3C\xB6\x6B\x23\x34\x67\x75\xD3\x86\xB5\x0E\x98\x2C\x49\x3A\xDB\xBF\xC5\x4B\x9A\x3C\xD3\x83\x38\x23\x36\xA1\xA0\xB2\x15\x0A"
	"\x15\x35\x8F\x33\x6D\x03\xAE\x18\xF6\x66\xC7\x57\x3D\x55\xC4\xFD\x18\x1C\x29\xE6\xCC\xFD\xE6\x3E\xA3\x5F\x0A\xDF\x58\x85\xCF\xC0"
	"\xA3\xD8\x4A\x2B\x2E\x4D\xD2\x44\x96\xDB\x78\x9E\x66\x31\x70\xCE\xF7\x47\x98\xAA\x1B\xBC\xD4\x57\x4E\xA0\xBB\xA4\x04\x89\xD7\x64"
	"\xB2\xF8\x3A\xAD\xC6\x6B\x14\x8B\x4A\x0C\xD9\x52\x46\xC1\x27\xD5\x87\x1C\x4F\x11\x41\x86\x90\xA5\xDD\xF0\x12\x46\xA0\xC8\x0A\x43"
	"\xC7\x00\x88\xB6\x18\x36\x39\xDC\xFD\xA4\x12\x5B\xD1\x13\xA8\xF4\x9E\xE2\x3E\xD3\x06\xFA\xAC\x57\x6C\x3F\xB0\xC1\xE2\x56\x67\x1D"
	"\x81\x7F\xC2\x53\x4A\x52\xF5\xB4\x39\xF7\x2E\x42\x4D\xE3\x76\xF4\xC5\x65\xCC\xA8\x23\x07\xDD\x9E\xF7\x6D\xA5\xB7\xC4\xEB\x7E\x08"
	"\x51\x72\xE3\x28\x80\x7C\x02\xD0\x11\xFF\xBF\x33\x78\x53\x78\xD7\x9D\xC2\x66\xF6\xA5\xBE\x6B\xB0\xE4\xA9\x2E\xCE\xEB\xAE\xB1";

#if        (cKeccakB    == 1600) && (cKeccakR == 1024)

const char	testVectorSqueezed[] PROGMEM = 
	"\x69\xE1\xB0\x72\xF0\xFF\x1D\xB6\xCD\x2E\x0A\xAA\x9F\x46\xDF\x68\x27\x33\x5D\x09\xD8\xAC\x9D\x0A\x50\x7F\x83\x01\x4E\x13\x76\xBD\x5E\xD7\x48\x61\xCF\xC3\xCB\x69\x9D\xDE\xE4\xAB\x45\x27\x73\x89\x33\x64\xD0\x11\xB4\x4F\x99\x43\x8B\x77\x60\xC5\xCD\xD3\xC0\x08\xF2\x0E\x23\x9F\x97\xBF\xBB\x35\x7E\x1D\x79\xD4\xAD\xE3\xBB\x28\x59\x82\x40\xC8\xD3\x8B\x21\x94\x91\x1B\x1E\xE1\xDC\x0B\xD0\xF8\xB1\x20\xD9\x8C\x0C\x24\x0F\x11\x95\xBF\xCD\x32\xBA\x91\xCB\xA0\x00\x37\x90\x55\xF0\xE3\xBF\xE6\x56\x68\xC7\xDB\x38\x9A\x78\x8A";

const char	testVectorAccumulated[] PROGMEM = 
	"\xFB\xEF\xD4\x59\x5E\x90\x4D\x4A\xA9\x27\x08\x4B\xE8\xDB\xAD\x78\x0B\x4F\xA9\xB3\xF6\x3D\x9E\x10\x63\x8A\x69\x42\x8D\x6F\x7D\xD6\x75\xDB\x97\x5F\x23\xFE\xED\x52\x35\xAD\xA9\x3D\xB3\x4C\x01\x37\x2A\x21\xF9\x13\xFE\x89\xE9\x77\x33\x34\xA1\x6E\x81\x46\x57\xB9\x3F\xDF\xB1\xA1\xB3\xB4\x00\x6F\xBC\x66\xEC\x19\x55\x44\xBF\x63\x6B\x04\x8B\x55\x37\xAE\xAA\x9C\x4E\xAC\x94\xA5\xBE\x29\x4C\xEC\x96\x36\x43\xB1\x42\xBF\xE3\xAD\x7B\x39\xC8\x4C\x1D\x17\x7B\xF5\x89\x78\x77\xD3\x1D\xCC\xC4\x1C\xAE\x8D\x42\x44\x79\xDA\x5F\x1D";

#elif    (cKeccakB    == 1600) && (cKeccakR == 1152)

const char	testVectorSqueezed[] PROGMEM = 
	"\x9C\x7D\xAD\xA8\x3C\xC4\x9D\x93\xC3\x72\x89\xBA\x7E\x89\x12\xB6\x97\x06\x50\xD7\xB7\x7C\xC8\xAC\x94\x0F\xCE\x52";

const char	testVectorAccumulated[] PROGMEM = 
	"\x22\xD2\x99\xD7\xB0\xBE\xA0\x03\x1E\x60\xFD\x8B\x4A\xDD\x41\x35\x8B\xAA\xF8\xB9\xAF\x4A\xAC\xE3\x20\xE2\x36\xD9";

#elif    (cKeccakB    == 1600) && (cKeccakR == 1088)

const char	testVectorSqueezed[] PROGMEM = 
	"\x94\x95\x79\x00\xE6\x48\xB7\xD1\x2C\x3C\x41\x14\xA4\xBF\xEE\xFF\x4C\xFA\xD2\x83\x1A\xEE\x86\xAD\x50\xB5\x8F\x8F\x5F\x09\xE3\x9D";

const char	testVectorAccumulated[] PROGMEM = 
	"\x75\x9E\x67\x68\x9D\xC5\xBA\xFE\xCB\x7B\x94\x4A\x5B\x4D\x33\xF0\xAC\xC1\xD2\x2E\xEF\x4E\xB1\x36\x36\x0B\xCB\xAB\x13\x79\x1E\x72";

#elif    (cKeccakB    == 1600) && (cKeccakR == 832)

const char	testVectorSqueezed[] PROGMEM = 
	"\x1B\xB8\x48\xA1\xF8\xA4\x0F\x4F\x55\x3B\xAF\xA3\x5A\x6A\xD7\x0C\x89\xB3\x99\xB5\xBD\x15\x59\xB6\x06\xD0\x6D\x2C\xA5\xFA\x39\x03\x91\xD7\xEE\xB9\x69\x46\xD7\x57\xD1\x65\x3C\x6C\x47\xEB\xED\x51";

const char	testVectorAccumulated[] PROGMEM = 
	"\x3D\x1E\xE6\x3A\x6D\x86\x80\x10\xD7\x72\xA5\x82\x11\xC4\x0E\x62\x95\x10\x5D\xED\x18\x5D\x87\xBB\xF9\xA2\x49\xBB\x4A\x53\x80\xC9\x21\x2A\x19\x55\xBC\xE1\x7E\xBC\x7A\xE4\xCE\xCC\x7B\x2C\x8C\x23";

#elif    (cKeccakB    == 1600) && (cKeccakR == 576)

const char	testVectorSqueezed[] PROGMEM = 
	"\x95\x7E\xD9\x15\x65\x2A\x0B\x00\x1C\x1D\x34\xC4\xE8\xD2\xE9\xA7\xD2\x4E\x09\x11\x82\xFD\x8E\x09\x7D\xEC\xAF\xC7\x38\x2B\x21\x60\x17\x32\x40\xCF\xD9\x84\x0E\xA0\x09\x46\x72\xCB\xD0\x01\xCE\x4C\xD2\x84\x05\x41\x1F\x1B\x8F\xCB\xCD\xDF\xB4\x8F\x6E\x1D\xEB\x39";

const char	testVectorAccumulated[] PROGMEM = 
	"\xF5\xB1\x00\x12\xCB\xE8\x75\x71\xCB\x72\x9B\x58\x25\xDF\x85\xCD\x23\x03\x62\x25\xAA\x9D\x5A\x6A\x70\xEB\x62\x23\xF6\x71\xC9\x6B\xD3\xEB\xE3\x32\x15\x69\x4B\x0F\x5B\xD9\x56\x6A\x33\x61\x1E\xF4\xBD\x8D\x69\x0B\xBC\x92\x7C\xAC\x4C\x31\x1D\x4F\x08\x6D\x00\x6A";

#endif

#define    cKeccakMessageSizeInBytes	(sizeof(testVectorMessage) - 1)
#define    cKeccakHashSizeInBytes			(sizeof(testVectorSqueezed) - 1)

unsigned char input[(cKeccakMessageSizeInBytes > 512) ? cKeccakMessageSizeInBytes : 512];
unsigned char output[cKeccakHashSizeInBytes];
unsigned char ref[cKeccakHashSizeInBytes];

int main( void )
{
	{
        unsigned char i;

		//	One test vector for crypto_hash()
		for ( i = 0; i < cKeccakMessageSizeInBytes; i++ )
		{
			input[i] = (unsigned char)pgm_read_byte(&testVectorMessage[i]);
		}

		crypto_hash( output, input, cKeccakMessageSizeInBytes );

		for ( i = 0; i < cKeccakHashSizeInBytes; i++ )
		{
			if ( output[i] != (unsigned char)pgm_read_byte(&testVectorSqueezed[i]) ) 
			{
				for ( ; ; );
			}
		}
	}

	{
		#define	refLen	cKeccakHashSizeInBytes
		unsigned int inlen, offset, size;
		int result;
		hashState			state;

		//	Acumulated test vector for crypto_hash()
		memset( ref, 0x00, sizeof(ref) );

		for ( inlen = 0; inlen <= (sizeof(input) * 8); (inlen < 2*8) ? inlen++ : ((inlen < 32*8) ? (inlen += 8) : (inlen <<= 1))  )
		{
			unsigned int i;
			unsigned int bytesize = (unsigned int)((inlen + 7) / 8);

			for ( i = 0; i < bytesize; ++i )
			{
				input[i] = (unsigned char)(i - bytesize);
			}

			result = Init( &state );
			if ( result != 0 )
			{
				for (;;);
			}

			for ( offset = 0; offset < inlen; offset += size )
			{
				//	vary sizes for Update()
				if ( (inlen %8) < 2 )
				{
					//	byte per byte
					size = 8;
				}
				else if ( (inlen %8) < 4 )
				{
					//	incremental
					size = offset + 8;
				}
				else
				{
					//	random
					size = ((rand() % ((inlen + 8) / 8)) + 1) * 8;
				}

				if ( size > (inlen - offset) ) 
				{
					size = inlen - offset;
				}
				result = Update( &state, input + offset / 8, size );
				if ( result != 0 )
				{
					for (;;);
				}
			}
			result = Final( &state, output, refLen );
			if ( result != 0 )
			{
				for (;;);
			}

			for ( i = 0; i < (unsigned int)refLen; ++i )
			{
				ref[i] ^= output[i];
			}
		}

        {
            unsigned char i;
            for ( i = 0; i < (unsigned char)refLen; i++ )
            {
                if ( ref[i] != (unsigned char)pgm_read_byte(&testVectorAccumulated[i]) ) 
                {
				    for ( ; ; );
                }
            }
        }
	}

	return ( 0 );
}
